Skip to content

Feature/new rectors 3#7

Closed
bbrala wants to merge 15 commits into
feature/new-rectors-2from
feature/new-rectors-3
Closed

Feature/new rectors 3#7
bbrala wants to merge 15 commits into
feature/new-rectors-2from
feature/new-rectors-3

Conversation

@bbrala
Copy link
Copy Markdown
Owner

@bbrala bbrala commented May 27, 2026

in progress

bbrala added 15 commits May 26, 2026 14:18
…torRector for issue #3559481

Removes the deprecated ImageToolkitInterface $toolkit 4th constructor
argument from ImageToolkitOperationBase subclasses and strips it from
the matching parent::__construct() call. Deprecated in drupal:11.4.0
and removed in drupal:13.0.0; the plugin manager now injects the
toolkit via setToolkit() after instantiation to enable constructor
autowiring.

The rector only fires when the subclass constructor has at least five
parameters, the 4th is typed exactly as
\Drupal\Core\ImageToolkit\ImageToolkitInterface, and the $toolkit
variable appears exactly once in the constructor body (as the 4th
argument of parent::__construct()). Classes that reference $toolkit
elsewhere in the constructor are left untouched.

@see https://www.drupal.org/node/3559481
@see https://www.drupal.org/node/3562304
…ame for issue #3573870

Drupal\user\Entity\EntityPermissionsRouteProviderWithCheck → EntityPermissionsRouteProvider.
Deprecated in drupal:11.1.0 and removed in drupal:12.0.0. The base provider has
existed since 10.0.x, so the rewrite is safe across all supported D10 and D11
minors. Registered via Rector's built-in RenameClassRector in
drupal-11.1-deprecations.php.

Access-check semantics: WithCheck layered a _custom_access requirement
(EntityPermissionsForm::access, also removed) on top of the base route to deny
access when an entity type had no entity-specific permissions; the base
provider already enforces _permission: administer permissions, so the security
boundary is preserved. Only the "no permissions defined → deny" convenience
check is dropped.

Limitation: doctrine annotation-string references (the dominant real-world
pattern) are not rewritten — RenameClassRector only touches PHP Name nodes
(use/extends/implements/::class/typehints), not strings inside annotations.
Contrib audit (api.tresbien.tech, 2026-05-27) found three modules using the
class (content_entity_builder, aiprompt, knowledge) and zero PHP-code
references, so this entry is mostly a safety net for use/extends/::class
patterns.
Rewrites the removed $dialog_options['dialogClass'] key to
$dialog_options['classes']['ui-dialog'] in OpenDialogCommand,
OpenModalDialogCommand, and OpenOffCanvasDialogCommand constructors. The
'dialogClass' option was deprecated in drupal:11.3.0 and removed in
drupal:12.0.0. classes['ui-dialog'] has existed in core since 10.3.x, so the
transformed output is safe across all drupal-rector–supported Drupal minors;
no BC wrapping is needed.

Handles three array shapes:
  - no existing 'classes' key → add 'classes' => ['ui-dialog' => $value]
  - 'classes' exists without 'ui-dialog' → insert 'ui-dialog' => $value
  - 'classes'['ui-dialog'] already present → concatenate string values

Non-literal options (variables, builder method returns) are skipped to avoid
guessing runtime values. Receiver narrowing is by FQCN match on the resolved
New_->class, so unrelated constructors with similarly-shaped option arrays are
left alone.

OpenModalDialogCommand coverage was added after the live test surfaced wide
contrib usage (more than OpenDialogCommand itself): it extends OpenDialogCommand
and forwards $dialog_options to parent::__construct(), so the same deprecation
fires from the parent.

Live-tested against ~2,230 PHP files in 17 D11-compatible contrib modules:
4 correct transformations (entity_browser, ai/field_widget_actions, ai_seo —
3 sites in a .module file), zero false-positives. Non-matching dialogClass
usages were JS-side data-dialog-options encoded strings, builder-method
returns, and assignment patterns — all out of scope for a structural rector.
Rewrites integer-literal `#access` values in render-array literals to
proper booleans: `1` (or any non-zero integer literal) becomes `true`,
`0` becomes `false`. Passing non-boolean, non-AccessResultInterface
values to `#access` was deprecated in drupal:11.4.0 and is removed in
drupal:13.0.0 (change record https://www.drupal.org/node/3549344).

The rule matches `ArrayItem` nodes whose key is the string literal
`'#access'` and whose value is an `Int_` (integer literal). It deliberately
leaves booleans, variables, ternaries, method calls, and any other
expression untouched — the correct boolean replacement for those cannot
be determined statically, and Drupal's runtime deprecation will still
prompt manual review.

The replacement is pure PHP (`true`/`false`), so no DeprecationHelper BC
wrapping is needed — the transformed code runs on every Drupal version.
Registered by default in config/drupal-11/drupal-11.4-deprecations.php.

BC risk assessment against installed contrib (paragraphs, webform,
ctools, field_group, metatag, address, commerce, custom_field,
search_api, linkit, ai, paragraphs_table, etc., ~3K PHP files): zero
integer-literal `#access` patterns remain in current contrib HEAD;
modules have already migrated to dynamic boolean expressions. The 186
distinct `'#access' => <expr>` patterns observed are all variables,
comparisons, or method calls — all correctly skipped by the rector.
…yzer change

rector/rector 2.4.5 (PR rectorphp/rector-src#8006) added a NodeNameResolver
constructor argument to Rector\NodeAnalyzer\ExprAnalyzer, breaking the
manual instantiation of BetterStandardPrinter in HookConvertRectorTest::setUp().

getLegacyHookFunction() never touches the printer, so bypass the constructor
with ReflectionClass::newInstanceWithoutConstructor() rather than tracking
upstream's transitive dependency chain (NodeNameResolver → ClassNaming /
CallAnalyzer → ReflectionProvider).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant